<template>
	<div class="tree-box">
		<div class="tree-search" v-if="enableFilter">
			<el-input v-model.trim="filterText" :placeholder="placeholder" clearable @input="searchTreeNode" maxlength="30">
				<template #suffix>
					<i class="iconfont icon-sousuo"></i>
				</template>
			</el-input>
		</div>
		<slot></slot>
		<el-scrollbar class="tree-scrollbar">
			<el-tree
				ref="treeRef"
				:data="treeData"
				:node-key="nodeKey"
				:props="defaultProps"
				:expand-on-click-node="false"
				:default-expand-all="isOpen"
				:default-expanded-keys="expandedNodes"
				:default-checked-keys="checkNodes"
				:empty-text="emptyText"
				:filter-node-method="filterNode"
				:lazy="lazyLoad"
				:load="loadNode"
				@node-click="nodeClick"
				:show-checkbox="showCheckbox"
				@check="checkedEvent"
				highlight-current
				:check-strictly="checkStrictly"
			/>
		</el-scrollbar>
	</div>
</template>

<script setup lang="ts">
import type { ElTree } from "element-plus";
import { debounce } from "@/utils/index";
interface Tree {
	[key: string]: any;
}
const props = defineProps({
	//每个树节点用来作为唯一标识的属性
	nodeKey: {
		type: String,
		default: "nodeCode"
	},
	//默认展开的节点
	defaultExpandedNodes: {
		type: Array,
		default: () => {
			return [];
		}
	},
	// 默认选中节点
	defaultCheckExpandedNodes: {
		type: Array,
		default: () => {
			return [];
		}
	},
	//配置选项
	defaultProps: {
		type: Object,
		default: () => {
			return {
				children: "children",
				label: "nodeName",
				isLeaf: "leaf"
			};
		}
	},
	// 树的数据
	treeData: {
		type: Array,
		default: () => {
			return [];
		}
	},
	// 是否展示多选框
	showCheckbox: {
		type: Boolean,
		default: false
	},
	// 是否需要懒加载
	lazyLoad: {
		type: Boolean,
		default: false
	},
	// 是否需要懒加载搜索
	lazySearch: {
		type: Boolean,
		default: false
	},
	// 是否展示搜索框
	enableFilter: {
		type: Boolean,
		default: false
	},
	// 是否展开节点
	isOpen: {
		type: Boolean,
		default: false
	},
	//搜索框的提示信息
	placeholder: {
		type: String,
		default: ""
	},
	// 是否严格遵循父子互相关联
	checkStrictly: {
		type: Boolean,
		default: true
	}
});
const emit = defineEmits(["node-click", "load", "check", "search"]);
const filterText = ref("");
const treeRef = ref<InstanceType<typeof ElTree>>();
const expandedNodes = computed<any>(() => props.defaultExpandedNodes);
const checkNodes = computed<any>(() => props.defaultCheckExpandedNodes);
const emptyText = ref("数据加载中..."); // // 数据为空时显示文本
watch(
	() => props.treeData,
	val => {
		if (val.length === 0) {
			emptyText.value = "暂无数据";
		}
	}
);

// 数组扁平化
const flattenArray = (array: any[]) => {
	return array.reduce((acc, item) => {
		if (Array.isArray(item.children)) {
			acc.push(...flattenArray(item.children));
		}
		acc.push(item);
		return acc;
	}, []);
};

//根据名称筛选字典树
const searchTreeNode = () => {
	if (filterText.value.startsWith(" ") || filterText.value.endsWith(" ")) return;
	if (!props.lazySearch) {
		treeRef.value?.filter(filterText.value);
		const flatTreeData = flattenArray(props.treeData);
		const filterData = flatTreeData.filter((item: any) => item[props.defaultProps.label].indexOf(filterText.value) !== -1);
		if (filterData.length === 0) {
			emptyText.value = "暂无数据";
		}
	} else {
		lazyLoadSearch();
	}
};
//懒加载搜索字典树防抖
const lazyLoadSearch = debounce(
	() => {
		emit("search", filterText.value);
	},
	500,
	false
);

const filterNode = (value: string, data: Tree, node: Tree) => {
	if (!value) return true;
	let parentNode = node.parent,
		labels = [node.label],
		level = 1;
	while (level < node.level) {
		labels = [...labels, parentNode.label];
		parentNode = parentNode.parent;
		level++;
	}
	return labels.some(label => label.indexOf(value) !== -1);
};
//懒加载树
const loadNode = (node: Tree, resolve: (data: Tree[]) => void) => {
	nextTick(() => {
		emit("load", node, resolve);
	});
};
//当节点被点击的时候触发
const nodeClick = (data: Tree) => {
	emit("node-click", data);
};
//点击节点复选框之后触发
const checkedEvent = (currentData: Tree, checkDatas: Tree) => {
	emit("check", currentData, checkDatas);
};
const resetFilterText = () => {
	filterText.value = "";
	searchTreeNode();
};
defineExpose({
	resetFilterText,
	filterText
});
</script>

<style scoped lang="scss">
.tree-box {
	width: 294px;
	height: 100%;
	margin-right: 12px;
	padding: 18px 20px;
	background-color: #fff;
	flex: none;
	display: flex;
	flex-direction: column;
	.tree-search {
		margin-bottom: 20px;
	}
	.tree-scrollbar {
		flex: 1;
	}
	:deep(.el-tree) {
		display: inline-block;
		min-width: 100%;
	}
}
</style>
